package com.zara.store.server;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;

import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.NonUniqueResultException;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.xml.transform.TransformerException;

import com.zara.store.common.EnvtHelper;
import com.zara.store.common.EnvtVO;
import com.zara.store.common.FileHelper;
import com.zara.store.common.IntegrationTransformer;
import com.zara.store.common.OfadVO;
import com.zara.store.common.PalcItemVO;
import com.zara.store.common.PalcItemViewVO;
import com.zara.store.common.PalcVO;
import com.zara.store.common.ProductVO;

public @Stateless class ProcessBusinessBean implements ProcessBusiness {
	
	@PersistenceContext(name="ZARA")
	private EntityManager entityManager;
	
	private static final Logger LOG = Logger.getLogger(ProcessBusinessBean.class.getName());

	@TransactionAttribute (TransactionAttributeType.REQUIRED)
	public OfadVO createOfad(OfadVO ofadVO) {
		FileHelper helper = new FileHelper("c:/zara-workspace/zara.process.ofad.log");
		Query query;

		helper.addLine("Clearing active OfAD");
		query = entityManager.createQuery("UPDATE Ofad o SET o.active = false ");
		query.executeUpdate();

		query = entityManager.createQuery("UPDATE Product o SET o.salePrice = null ");
		query.executeUpdate();
		
		helper.addLine("Creating new OfAD: " + ofadVO.getDate());
		Ofad ofad = new Ofad(ofadVO.getDate());
		entityManager.persist(ofad);
		
		OfadVO retval = new OfadVO();
		for (ProductVO productVO: ofadVO.getItems()) {
			Product product = queryProduct(productVO.getBarcode());
			boolean isNew = product == null;
			if(isNew) {
				helper.addLine("New Product: " + productVO.getBarcode());
				if ("Accessory".equals(productVO.getType())) {
					product = new Accessory(productVO);
				} else {
					product = new Apparel(productVO);
				}
			} else {
				helper.addLine("Updated Product: " + productVO.getBarcode());
			}
			product.setSalePrice(productVO.getSalePrice());
			ProductVO toVO = product.toVO();
			if(isNew) {
				retval.addNew(toVO);
				entityManager.persist(product);
			} else {
				entityManager.merge(product);
			}
			retval.add(toVO);

			OfadItem ofadItem = new OfadItem(ofad, product);
			entityManager.persist(ofadItem);
		}
		
		return retval;
	}

	@TransactionAttribute (TransactionAttributeType.REQUIRED)
	public EnvtVO createEnvt(EnvtVO envtVO) {
		FileHelper helper = new FileHelper("c:/zara-workspace/zara.process.envt.log");
		Palc palc = queryPalc(envtVO);
		if(palc == null) {
			helper.addLine("Looking for most recent PaLC, missing PaLC: " + envtVO.getDate());
			palc = queryMostRecentPalc();
		}
		if(palc != null) {
			Envt envt = new Envt(envtVO);
			entityManager.persist(envt);
			EnvtVO envtVO2 = envt.toVO();
			for(ProductVO productVO: envtVO.getItems()) {
				Product product = queryProduct(productVO.getBarcode());
				if(product != null) {
					BigDecimal amount = productVO.getAmount();
					BigDecimal newStock = product.getStock().add(amount);
					LOG.info(productVO.getBarcode() + ", amount: " + amount + ", newStock: " + newStock);
					product.setStock(newStock);

					entityManager.merge(product);
					PalcItem palcItem = queryPalcItem(palc, product);
					if(palcItem != null) {
						BigDecimal amountPreviouslyReceived = palcItem.getAmountReceived();
						BigDecimal totalAmountReceived = amountPreviouslyReceived.add(amount);
						palcItem.setAmountReceived(totalAmountReceived);
						entityManager.merge(palcItem);
						BigDecimal amountRemaining = palcItem.getAmountRequested().subtract(totalAmountReceived);
						LOG.info(productVO.getBarcode() + ", amountRequested: " + palcItem.getAmountRequested() + ", totalAmountReceived: " + totalAmountReceived);
						helper.addLine(productVO.getBarcode() + ", amountRequested: " + palcItem.getAmountRequested() + ", totalAmountReceived: " + totalAmountReceived);
						
						ProductVO pVO = product.toVO();
						if(amountRemaining.compareTo(BigDecimal.ZERO) > 0) {
							pVO.setAmount(amountRemaining);
							envtVO2.addPending(pVO);
							helper.addLine("Pending Product: " + productVO.getBarcode() + ", pendingAmount: " + amountRemaining);
						} else {
							pVO.setAmount(amount);
							envtVO2.add(pVO);
						}
					} else {
						helper.addLine("Missing PaLC item: " + envtVO.getDate() + ", " + productVO.getBarcode());
					}
					EnvtItem envtItem = new EnvtItem(envt, product, amount);
					entityManager.persist(envtItem);
				} else {
					helper.addLine("Missing Product: " + productVO.getBarcode());
					
				}
			}
			return envtVO2;
		} else {
			helper.addLine("No recent PaLC, missing PaLC: " + envtVO.getDate());
		}
		return envtVO;
	}

	private Palc queryMostRecentPalc() {
		Query palcQuery = entityManager.createQuery("FROM Palc palc ORDER BY palc.palcDate DESC");
		try {
			List list = palcQuery.getResultList();
			if(list != null && list.size()>0) {
				return (Palc) list.get(0);
			}
		} catch(NoResultException e) {
			//TODO: log error
		} catch(NonUniqueResultException e) {
			//TODO: log error
		}
		return null;
	}

	private Palc queryPalc(EnvtVO envtVO) {
		Query palcQuery = entityManager.createQuery("FROM Palc palc WHERE palc.palcDate = ?");
		palcQuery.setParameter(1, envtVO.getDate());
		try {
			return (Palc) palcQuery.getSingleResult();
		} catch(NoResultException e) {
			//TODO: log error
		} catch(NonUniqueResultException e) {
			//TODO: log error
		}
		return null;
	}

	private PalcItem queryPalcItem(Palc palc, Product product) {
		Query palcQuery = entityManager.createQuery("FROM PalcItem palc WHERE palc.palc = ? and palc.product = ?");
		palcQuery.setParameter(1, palc);
		palcQuery.setParameter(2, product);
		try {
			return (PalcItem) palcQuery.getSingleResult();
		} catch(NoResultException e) {
			//TODO: log error
		} catch(NonUniqueResultException e) {
			//TODO: log error
		}
		return null;
	}

	private Product queryProduct(String barcode) {
		Query query = entityManager.createQuery("FROM Product product WHERE product.barcode = ?");
		query.setParameter(1, barcode);
		try {
			return (Product) query.getSingleResult();
		} catch(NoResultException e) {
			//TODO: log error
		} catch(NonUniqueResultException e) {
			//TODO: log error
		}
		return null;
	}

	public Collection<PalcItemViewVO> queryPalcProductList() {
		Map<Product,PalcItemViewVO> map = new HashMap<Product,PalcItemViewVO>();

		//disponibles en la ofad
		Query availableInOfadQuery = entityManager.createQuery("" +
				//"FROM Product p WHERE p.salePrice != null"
				"SELECT oitem.product " +
				"FROM OfadItem oitem " +
				"INNER JOIN oitem.ofad ofad " +
				"WHERE ofad.active = true"
		);
		List availableInOfadList = availableInOfadQuery.getResultList();
		for(Object i: availableInOfadList) {
			Product product = (Product)i;
			PalcItemViewVO mapItem = map.get(product);
			if(mapItem!=null) {
				// nothing;
			} else {
				PalcItemViewVO viewVO = new PalcItemViewVO();
				viewVO.setAvailableInOfad(true);
				viewVO.setProduct(product.toVO());
				map.put(product, viewVO);
			}
		}

		//vendidos en la ultima semana y cantidad vendida
		Query soldLastWeekQuery = entityManager.createQuery("" +
				//original
				//"SELECT item.product, sum(item.amount) " +
				//fix mssql
				"SELECT item.product.id, sum(item.amount) " +
				"FROM InvoiceItem item " +
				"INNER JOIN item.invoice invoice " +
				"WHERE invoice.invoiceDate > ? " +
				"GROUP BY item.product");
		Calendar lastWeekDate = Calendar.getInstance();
		lastWeekDate.add(Calendar.WEEK_OF_MONTH, -1);
		soldLastWeekQuery.setParameter(1, lastWeekDate.getTime());
		List soldLastWeekList = soldLastWeekQuery.getResultList();
		for(Object i: soldLastWeekList) {
			Object[] row = (Object[])i;
			//original
			//Product product = (Product)row[0];
			//fix mssql
			Product product = entityManager.find(Product.class, (Integer)row[0]);
			BigDecimal amountSoldLastWeek = new BigDecimal(row[1].toString());
			PalcItemViewVO mapItem = map.get(product);
			if(mapItem!=null) {
				mapItem.setAmountSoldLastWeek(mapItem.getAmountSoldLastWeek().add(amountSoldLastWeek));
			} else {
				PalcItemViewVO viewVO = new PalcItemViewVO();
				viewVO.setProduct(product.toVO());
				viewVO.setAmountSoldLastWeek(amountSoldLastWeek);
				map.put(product, viewVO);
			}
		}
		
		Query pendingOnPalcQuery = entityManager.createQuery("" +
				//original
				//"SELECT item.product, sum(item.amountRequested-item.amountReceived) " +
				//fix mssql
				"SELECT item.product.id, sum(item.amountRequested-item.amountReceived) " +
				"FROM PalcItem item WHERE item.amountRequested > item.amountReceived " +
				"GROUP BY item.product");
		List pendingOnPalcList = pendingOnPalcQuery.getResultList();
		for(Object i: pendingOnPalcList) {
			Object[] row = (Object[])i;
			//original
			//Product product = (Product)row[0];
			//fix mssql
			Product product = entityManager.find(Product.class, (Integer)row[0]);
			BigDecimal amountPendingOnPalc = new BigDecimal(row[1].toString());
			PalcItemViewVO mapItem = map.get(product);
			if(mapItem!=null) {
				mapItem.setAmountPendingOnLastPalc(mapItem.getAmountPendingOnLastPalc().add(amountPendingOnPalc));
			} else {
				PalcItemViewVO viewVO = new PalcItemViewVO();
				viewVO.setProduct(product.toVO());
				viewVO.setAmountPendingOnLastPalc(amountPendingOnPalc);
				map.put(product, viewVO);
			}
		}
		
		Query productStockQuery = entityManager.createQuery("" +
				"FROM Product p WHERE p.stock < p.orderStock ");
		List productStockList = productStockQuery.getResultList();
		for(Object i: productStockList) {
			Product product = (Product)i;
			PalcItemViewVO mapItem = map.get(product);
			if(mapItem!=null) {
				// nothing;
			} else {
				PalcItemViewVO viewVO = new PalcItemViewVO();
				viewVO.setProduct(product.toVO());
				map.put(product, viewVO);
			}
		}
		
		List<PalcItemViewVO> items = new ArrayList<PalcItemViewVO>(map.size());
		items.addAll(map.values());
		return Collections.unmodifiableCollection(items);
	}
	
	@TransactionAttribute (TransactionAttributeType.REQUIRED)
	public PalcVO createPalc(List<PalcItemVO> items) {
		Palc palc = new Palc();
		entityManager.persist(palc);
		PalcVO palcVO = palc.toVO();
		for(PalcItemVO p: items) {
			PalcItem item = new PalcItem();
			Product product = entityManager.find(Product.class, p.getProductId());
			item.setAmountRequested(p.getAmount());
			item.setPalc(palc);
			item.setProduct(product);
			entityManager.persist(item);
			palcVO.addItem(item.toVO());
		}
		return palcVO;
	}
	
	@TransactionAttribute (TransactionAttributeType.REQUIRED)
	public EnvtVO createEnvtFromXml(String xml) {
		EnvtVO envtVO;
		try {
			envtVO = new EnvtHelper().createEnvtFromXml(new IntegrationTransformer().transform(xml));
			return createEnvt(envtVO);
		} catch (TransformerException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}
	
}
